using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using LitJson;
using UnityEngine;
using UnityEngine.Networking;
using Random = UnityEngine.Random;

namespace CGF
{
    public class HttpRes
    {
        public string url,text,error;
        public bool success;
        public byte[] bytes;

        public HttpRes(string url)
        {
            this.url = url;
        }
    }

    public class GetTask : MonoBehaviour
    {
        private UnityWebRequest request;
        private Action<float> onProgress;

        public async Task<HttpRes> Get(string url, Action<float> onProgress, int timeoutSeconds)
        {
            this.onProgress = onProgress;
            using (request = UnityWebRequest.Get(url))
            {
                request.timeout = timeoutSeconds;
                string token = PlayerPrefs.GetString("access_token");
                if (!string.IsNullOrEmpty(token))
                {
                    request.SetRequestHeader("Authorization", $"Bearer {token}");
                }

                await request.SendWebRequest();
                var httpRes = new HttpRes(url);
                if (request.result != UnityWebRequest.Result.Success)
                {
                    httpRes.success = false;
                    httpRes.text = request.downloadHandler.text;
                    httpRes.error = "[" + request.responseCode + "]" + request.error + " " + request.downloadHandler.text;
                }
                else
                {
                    httpRes.success = true;
                    httpRes.bytes = request.downloadHandler.data;
                    httpRes.text = request.downloadHandler.text;
                    onProgress(1);
                }

                if (Debug.canLog)
                {
                    HttpHelper.LogRes("GET", httpRes);
                }
                Destroy(gameObject);
                return httpRes;
            }
        }

        private void Update()
        {
            if (request != null && onProgress != null)
            {
                if (request.downloadProgress < 1f)
                {
                    onProgress(request.downloadProgress);
                }
            }
        }
    }

    public static class HttpHelper
    {
        //扩展UnityWebRequest方法使其支持await
        public static async CTask GetAwaiter(this AsyncOperation asyncOperation)
        {
            CTask task = CTask.Create();
            asyncOperation.completed += ao => { task.SetResult(); };
            await task;
        }

        public static async CTask<HttpRes> Get(string url, Action<float> onProgress = null, int timeoutSeconds = 10)
        {
            Debug.Log($"GET -> {url}");
            if (onProgress != null)
            {
                GameObject go = new GameObject("HttpGetTask");
                GameObject.DontDestroyOnLoad(go);
                var task = go.AddComponent<GetTask>();
                return await task.Get(url, onProgress, timeoutSeconds);
            }
            else
            {
                using (var request = UnityWebRequest.Get(NoCache(url)))
                {
                    request.timeout = timeoutSeconds;
                    string token = PlayerPrefs.GetString("access_token");
                    if (!string.IsNullOrEmpty(token))
                    {
                        request.SetRequestHeader("Authorization", $"Bearer {token}");
                    }

                    await request.SendWebRequest();
                    var httpRes = new HttpRes(url);
                    if (request.result != UnityWebRequest.Result.Success)
                    {
                        httpRes.success = false;
                        httpRes.text = request.downloadHandler.text;
                        httpRes.error = "[" + request.responseCode + "]" + request.error + " " + request.downloadHandler.text;
                    }
                    else
                    {
                        httpRes.success = true;
                        httpRes.bytes = request.downloadHandler.data;
                        httpRes.text = request.downloadHandler.text;
                    }

                    if (Debug.canLog)
                    {
                        LogRes("GET", httpRes);
                    }
                    return httpRes;
                }
            }
        }

        public static async CTask<HttpRes> PostJson(string url, JsonData data, int timeoutSeconds = 10)
        {
            return await Request(url, "POST", data, timeoutSeconds);
        }

        public static async CTask<HttpRes> PostForm(string url, Dictionary<string, object> data, int timeoutSeconds = 10)
        {
            return await Request(url, "POST", data, timeoutSeconds);
        }

        public static async CTask<HttpRes> PutJson(string url, JsonData data, int timeoutSeconds = 10)
        {
            return await Request(url, "PUT", data, timeoutSeconds);
        }

        public static async CTask<HttpRes> PutForm(string url, Dictionary<string, object> data, int timeoutSeconds = 10)
        {
            return await Request(url, "PUT", data, timeoutSeconds);
        }

        public static async CTask<HttpRes> DeleteJson(string url, JsonData data, int timeoutSeconds = 10)
        {
            return await Request(url, "DELETE", data, timeoutSeconds);
        }

        public static async CTask<HttpRes> DeleteForm(string url, Dictionary<string, object> data, int timeoutSeconds = 10)
        {
            return await Request(url, "DELETE", data, timeoutSeconds);
        }

        private static async CTask<HttpRes> Request(string url, string method, object data, int timeoutSeconds = 10)
        {
            Debug.Log($"{method} -> {url} {Regex.Unescape(data.ToJson())}");
            using (UnityWebRequest request = new UnityWebRequest(NoCache(url), method))
            {
                request.downloadHandler = new DownloadHandlerBuffer();
                request.timeout = timeoutSeconds;
                byte[] bodyRaw;
                if (data is JsonData)
                {
                    request.SetRequestHeader("Content-Type", "application/json;charset=utf-8");
                    bodyRaw = Encoding.UTF8.GetBytes(data.ToJson());
                }
                else
                {
                    request.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
                    Dictionary<string, string> dic = new();
                    if (data != null)
                    {
                        foreach (var item in (Dictionary<string, object>)data)
                        {
                            dic.Add(item.Key, item.Value != null ? item.Value.ToString() : "");
                        }
                    }

                    bodyRaw = UnityWebRequest.SerializeSimpleForm(dic);
                }

                string token = PlayerPrefs.GetString("access_token");
                if (!string.IsNullOrEmpty(token))
                {
                    request.SetRequestHeader("Authorization", $"Bearer {token}");
                }

                request.uploadHandler?.Dispose();
                request.uploadHandler = new UploadHandlerRaw(bodyRaw);
                await request.SendWebRequest();
                var httpRes = new HttpRes(url);
                if (request.result != UnityWebRequest.Result.Success)
                {
                    httpRes.success = false;
                    httpRes.text = request.downloadHandler.text;
                    httpRes.error = "[" + request.responseCode + "]" + request.error + " " + request.downloadHandler.text;
                }
                else
                {
                    httpRes.success = true;
                    httpRes.bytes = request.downloadHandler.data;
                    httpRes.text = request.downloadHandler.text;
                }

                request.disposeDownloadHandlerOnDispose = true;
                request.disposeUploadHandlerOnDispose = true;
                if (Debug.canLog)
                {
                    LogRes(method, httpRes);
                }

                return httpRes;
            }
        }

        private static string NoCache(string url)
        {
            if (url.Contains("?"))
            {
                return url + "&r=" + Random.Range(0, 100);
            }
            else
            {
                return url + "?r=" + Random.Range(0, 100);
            }
        }

        public static void LogRes(string method, HttpRes httpRes)
        {
            JsonData resForLog = new JsonData();
            resForLog["url"] = httpRes.url;
            resForLog["success"] = httpRes.success;
            resForLog["text"] = httpRes.text;
            resForLog["bytes"] = $"[length:{(httpRes.bytes != null ? httpRes.bytes.Length : 0)}]";
            Debug.Log($"{method} <- {Regex.Unescape(resForLog.ToJson())}");
        }

        /// <summary>
        /// 将文本转为Url编码
        /// </summary>
        public static string URLEncode(string str)
        {
            return UnityWebRequest.EscapeURL(str);
        }

        /// <summary>
        /// 将Url编码转成普通文本
        /// </summary>
        public static string URLDecode(string str)
        {
            return UnityWebRequest.UnEscapeURL(str);
        }
    }
}